package com.ccb.sc.auth;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ccb.sc.constant.AppConst;
//psl import com.ccb.sc.common.bind.exception.BizException;
import com.ccb.sc.common.code.BizCodeFace;
import com.ccb.sc.common.code.ErrorCode;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ken
 * @version 1.0
 * @date 2020-09-14
 */
@Component
public class JWTHelper {

    private static final String SECRET = "XX#$%()(#*!()!KL<><sdys9seBs dsddsgdsddddd>?N<:{LWPW";

    public static String sign(long userId, String tokenType, String sessionKey) {
        return sign(userId, tokenType, sessionKey, null);
    }

    public static String sign(long userId, String tokenType, String sessionKey, Long expiryHours){
        LocalDateTime now = LocalDateTime.now();

        // header Map
        Map<String, Object> map = new HashMap<>(2);
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        // build token
        // param backups {iss:Service, aud:APP}
        /*
        iss: jwt签发者
        sub: jwt所面向的用户
        aud: 接收jwt的一方
        exp: jwt的过期时间，这个过期时间必须要大于签发时间
        nbf: 定义在什么时间之前，该jwt都是不可用的.
        iat: jwt的签发时间
        jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
        */
        try {
            JWTCreator.Builder builder = JWT.create().withHeader(map)
                    .withClaim("iss", "ALL-GIRLS")
                    .withClaim("sub", "web-app")
                    .withClaim("aud", tokenType)
                    .withClaim("userId", userId)
                    .withClaim(AppConst.MEMBER_SESSION_ATTR_KEY, sessionKey)
                    .withIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
            if (expiryHours != null) {
                // 过期时间
                Date expiresDate = Date.from(now.plusHours(expiryHours).atZone(ZoneId.systemDefault()).toInstant());
                builder.withExpiresAt(expiresDate);
            }
            return builder.sign(Algorithm.HMAC256(SECRET));
        } catch (Exception e) {
            //psl throw new BizException(BizCodeFace.createBizCode(ErrorCode.DATE_NULL));
        }
        return null;
    }

    public static String sign(Map<String, String> claims, Long expirySeconds){
        LocalDateTime now = LocalDateTime.now();

        // header Map
        Map<String, Object> map = new HashMap<>(2);
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        // build token
        // param backups {iss:Service, aud:APP}
        /*
        iss: jwt签发者
        sub: jwt所面向的用户
        aud: 接收jwt的一方
        exp: jwt的过期时间，这个过期时间必须要大于签发时间
        nbf: 定义在什么时间之前，该jwt都是不可用的.
        iat: jwt的签发时间
        jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
        */
        try {
            JWTCreator.Builder builder = JWT.create().withHeader(map)
                    .withClaim("iss", "DTY")
                    .withClaim("sub", "web-app")
                    .withIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()));
            if (!claims.isEmpty()) {
                claims.forEach(builder::withClaim);
            }
            if (expirySeconds != null) {
                // 过期时间
                Date expiresDate = Date.from(now.plusSeconds(expirySeconds).atZone(ZoneId.systemDefault()).toInstant());
                builder.withExpiresAt(expiresDate);
            }
            return builder.sign(Algorithm.HMAC256(SECRET));
        } catch (Exception e) {
            //psl throw new BizException(BizCodeFace.createBizCode(ErrorCode.DATE_NULL));
        }
        return null;
    }

    public static Map<String, Claim> unSign(String token){
        DecodedJWT jwt;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            jwt = verifier.verify(token);
        } catch (Exception e) {
            //psl throw new BizException(BizCodeFace.createBizCode(ErrorCode.PARAM_ERROR).message("token error"));
            return null;
        }
        return jwt.getClaims();
    }

    public static void main(String[] args){
        Map<String, String> claims = new HashMap<>();
        claims.put("phone", "17620370423");
        claims.put("password", "123456");
        claims.put("aud", AppConst.PREFIX_TOKEN_TYPE);
        String token = sign(claims, 100L);
        System.out.println(token);
        Map<String, Claim> decodeToken = unSign(token);
        System.out.println(decodeToken);
        Claim aud = decodeToken.get("aud");
        System.out.println("aud:"+aud.asString());
        Claim iss = decodeToken.get("iss");
        System.out.println("iss:"+iss.asString());
        Claim exp = decodeToken.get("exp");
        System.out.println("exp:"+exp.asDate());
        Claim iat = decodeToken.get("iat");
        System.out.println("iat:"+iat.asDate());
        Claim phone = decodeToken.get("phone");
        System.out.println("phone:" + phone.asString());
    }
}
